"""
Auth tests for /api/v1/audio/transcriptions and /api/v1/audio/translations.
Uses dependency overrides to simulate authenticated user and monkeypatches
transcription functions to avoid heavy model calls.
"""

import io
import math
import struct
import wave

import pytest
from fastapi.testclient import TestClient
from tldw_Server_API.app.api.v1.endpoints import audio as audio_endpoints

from tldw_Server_API.app.main import app
from tldw_Server_API.app.core.AuthNZ.User_DB_Handling import User, get_request_user

pytestmark = [pytest.mark.integration]


def _make_wav_bytes(duration_sec: float = 0.1, sr: int = 16000, freq: float = 440.0) -> bytes:
    """Generate a small mono 16-bit PCM WAV for testing."""
    n_samples = int(duration_sec * sr)
    buf = io.BytesIO()
    with wave.open(buf, 'wb') as wf:
        wf.setnchannels(1)
        wf.setsampwidth(2)
        wf.setframerate(sr)
        frames = bytearray()
        for i in range(n_samples):
            sample = int(32767 * 0.2 * math.sin(2 * math.pi * freq * (i / sr)))
            frames += struct.pack('<h', sample)
        wf.writeframes(frames)
    return buf.getvalue()


def test_transcriptions_requires_auth_401(bypass_api_limits):
    ctx = bypass_api_limits(app, limiters=(audio_endpoints.limiter,))
    with ctx, TestClient(app) as client:
        wav_bytes = _make_wav_bytes()
        files = {"file": ("test.wav", wav_bytes, "audio/wav")}
        data = {"model": "whisper-1", "response_format": "json"}
        resp = client.post("/api/v1/audio/transcriptions", files=files, data=data)
        assert resp.status_code == 401


def test_transcriptions_ok_with_override(monkeypatch, bypass_api_limits):
    ctx = bypass_api_limits(app, limiters=(audio_endpoints.limiter,))
    with ctx, TestClient(app) as client:
        async def _override_user():
            return User(id=1, username="tester", email="t@example.com", is_active=True)

        app.dependency_overrides[get_request_user] = _override_user

        # Patch the production function used by the endpoint (Whisper path)
        def _fake_speech_to_text(*args, **kwargs):
            # Endpoint expects a tuple (segments_list, detected_language) when return_language=True
            segments = [
                {"start_seconds": 0.0, "end_seconds": 0.1, "Text": "stubbed transcript"}
            ]
            return (segments, "en")

        monkeypatch.setattr(
            "tldw_Server_API.app.core.Ingestion_Media_Processing.Audio.Audio_Transcription_Lib.speech_to_text",
            _fake_speech_to_text,
            raising=False,
        )

        # Pretend the Whisper model is already available so the new
        # preflight check does not short-circuit with a 503.
        from tldw_Server_API.app.core.Ingestion_Media_Processing.Audio import Audio_Files as audio_files

        def _always_available_status(model_name: str):
            return {
                "available": True,
                "message": f"Model {model_name} is available and ready for use",
                "model": model_name,
            }

        monkeypatch.setattr(
            audio_files,
            "check_transcription_model_status",
            _always_available_status,
            raising=True,
        )

        try:
            wav_bytes = _make_wav_bytes()
            files = {"file": ("test.wav", wav_bytes, "audio/wav")}
            data = {"model": "whisper-1", "response_format": "json"}
            resp = client.post("/api/v1/audio/transcriptions", files=files, data=data)
            assert resp.status_code == 200
            assert resp.json().get("text") == "stubbed transcript"
        finally:
            app.dependency_overrides.pop(get_request_user, None)


def test_translations_requires_auth_401(bypass_api_limits):
    ctx = bypass_api_limits(app, limiters=(audio_endpoints.limiter,))
    with ctx, TestClient(app) as client:
        wav_bytes = _make_wav_bytes()
        files = {"file": ("test.wav", wav_bytes, "audio/wav")}
        data = {"model": "whisper-1", "response_format": "json"}
        resp = client.post("/api/v1/audio/translations", files=files, data=data)
        assert resp.status_code == 401


def test_translations_ok_with_override(monkeypatch, bypass_api_limits):
    ctx = bypass_api_limits(app, limiters=(audio_endpoints.limiter,))
    with ctx, TestClient(app) as client:
        async def _override_user():
            return User(id=1, username="tester", email="t@example.com", is_active=True)

        app.dependency_overrides[get_request_user] = _override_user

        # Patch the production function used by the endpoint (Whisper path)
        def _fake_speech_to_text(*args, **kwargs):
            segments = [
                {"start_seconds": 0.0, "end_seconds": 0.1, "Text": "translated transcript"}
            ]
            return (segments, "en")

        monkeypatch.setattr(
            "tldw_Server_API.app.core.Ingestion_Media_Processing.Audio.Audio_Transcription_Lib.speech_to_text",
            _fake_speech_to_text,
            raising=False,
        )

        # Pretend the Whisper model is already available so the preflight
        # check does not cause a 503 in tests.
        from tldw_Server_API.app.core.Ingestion_Media_Processing.Audio import Audio_Files as audio_files

        def _always_available_status(model_name: str):
            return {
                "available": True,
                "message": f"Model {model_name} is available and ready for use",
                "model": model_name,
            }

        monkeypatch.setattr(
            audio_files,
            "check_transcription_model_status",
            _always_available_status,
            raising=True,
        )

        try:
            wav_bytes = _make_wav_bytes()
            files = {"file": ("test.wav", wav_bytes, "audio/wav")}
            data = {"model": "whisper-1", "response_format": "json"}
            resp = client.post("/api/v1/audio/translations", files=files, data=data)
            assert resp.status_code == 200
            assert resp.json().get("text") == "translated transcript"
        finally:
            app.dependency_overrides.pop(get_request_user, None)


def test_transcriptions_parakeet_variant_routes_to_parakeet(monkeypatch, bypass_api_limits):
    """Model strings like 'parakeet-mlx' should route to the Parakeet provider, not Whisper."""
    ctx = bypass_api_limits(app, limiters=(audio_endpoints.limiter,))
    with ctx, TestClient(app) as client:
        async def _override_user():
            return User(id=1, username="tester", email="t@example.com", is_active=True)

        app.dependency_overrides[get_request_user] = _override_user

        # Force lightweight config for Nemo path
        monkeypatch.setattr(
            "tldw_Server_API.app.core.config.load_and_log_configs",
            lambda: {"STT-Settings": {"nemo_model_variant": "standard"}},
            raising=False,
        )

        # If Whisper path is hit incorrectly, fail fast
        def _fail_speech_to_text(*args, **kwargs):
            raise AssertionError("Whisper path should not be used for parakeet-mlx")

        monkeypatch.setattr(
            "tldw_Server_API.app.core.Ingestion_Media_Processing.Audio.Audio_Transcription_Lib.speech_to_text",
            _fail_speech_to_text,
            raising=False,
        )

        # Parakeet Nemo implementation stub
        def _fake_parakeet(audio_data, sample_rate, variant):
            return "parakeet transcript"

        monkeypatch.setattr(
            "tldw_Server_API.app.core.Ingestion_Media_Processing.Audio.Audio_Transcription_Nemo.transcribe_with_parakeet",
            _fake_parakeet,
            raising=False,
        )

        try:
            wav_bytes = _make_wav_bytes()
            files = {"file": ("test.wav", wav_bytes, "audio/wav")}
            data = {"model": "parakeet-mlx", "response_format": "json"}
            resp = client.post("/api/v1/audio/transcriptions", files=files, data=data)
            assert resp.status_code == 200
            assert resp.json().get("text") == "parakeet transcript"
        finally:
            app.dependency_overrides.pop(get_request_user, None)


def test_transcriptions_qwen2audio_variant_routes_to_qwen2audio(monkeypatch, bypass_api_limits):
    """Model strings like 'qwen2audio-test' should route to Qwen2Audio provider, not Whisper."""
    ctx = bypass_api_limits(app, limiters=(audio_endpoints.limiter,))
    with ctx, TestClient(app) as client:
        async def _override_user():
            return User(id=1, username="tester", email="t@example.com", is_active=True)

        app.dependency_overrides[get_request_user] = _override_user

        # Fail if faster-whisper path is chosen
        def _fail_speech_to_text(*args, **kwargs):
            raise AssertionError("Whisper path should not be used for qwen2audio-*")

        monkeypatch.setattr(
            "tldw_Server_API.app.core.Ingestion_Media_Processing.Audio.Audio_Transcription_Lib.speech_to_text",
            _fail_speech_to_text,
            raising=False,
        )

        # General transcribe_audio stub for qwen2audio provider
        from tldw_Server_API.app.core.Ingestion_Media_Processing.Audio import Audio_Transcription_Lib as ATL

        def _fake_transcribe_audio(audio_data, transcription_provider, sample_rate=16000, speaker_lang=None, whisper_model="distil-large-v3"):
            # Ensure we are invoked with the expected provider
            assert transcription_provider == "qwen2audio"
            return "qwen2audio transcript"

        monkeypatch.setattr(ATL, "transcribe_audio", _fake_transcribe_audio, raising=True)

        try:
            wav_bytes = _make_wav_bytes()
            files = {"file": ("test.wav", wav_bytes, "audio/wav")}
            data = {"model": "qwen2audio-test", "response_format": "json"}
            resp = client.post("/api/v1/audio/transcriptions", files=files, data=data)
            assert resp.status_code == 200
            assert resp.json().get("text") == "qwen2audio transcript"
        finally:
            app.dependency_overrides.pop(get_request_user, None)


def test_transcriptions_whisper_model_unavailable_returns_503(monkeypatch, bypass_api_limits):
    """
    When the underlying faster-whisper model is not available locally,
    /audio/transcriptions should surface a structured 503 instead of
    returning a pseudo-transcript that clients might persist.
    """
    ctx = bypass_api_limits(app, limiters=(audio_endpoints.limiter,))
    with ctx, TestClient(app) as client:
        async def _override_user():
            return User(id=1, username="tester", email="t@example.com", is_active=True)

        app.dependency_overrides[get_request_user] = _override_user

        # Fail if the heavy Whisper STT path is invoked; the preflight
        # should short-circuit before speech_to_text is called.
        def _fail_speech_to_text(*args, **kwargs):
            raise AssertionError("speech_to_text should not be called when model is unavailable")

        monkeypatch.setattr(
            "tldw_Server_API.app.core.Ingestion_Media_Processing.Audio.Audio_Transcription_Lib.speech_to_text",
            _fail_speech_to_text,
            raising=False,
        )

        # Pretend the canonical Whisper model is not yet available locally
        from tldw_Server_API.app.core.Ingestion_Media_Processing.Audio import Audio_Files as audio_files

        def _fake_check_status(model_name: str):
            return {
                "available": False,
                "message": f"Model {model_name} is not available locally and will be downloaded.",
                "model": model_name,
                "estimated_size": "10 GB",
            }

        monkeypatch.setattr(audio_files, "check_transcription_model_status", _fake_check_status, raising=True)

        try:
            wav_bytes = _make_wav_bytes()
            files = {"file": ("test.wav", wav_bytes, "audio/wav")}
            data = {"model": "whisper-1", "response_format": "json"}
            resp = client.post("/api/v1/audio/transcriptions", files=files, data=data)
            assert resp.status_code == 503
            body = resp.json()
            assert isinstance(body, dict)
            detail = body.get("detail") or {}
            assert detail.get("status") == "model_downloading"
            assert "not available locally" in detail.get("message", "")
            assert detail.get("model") == "large-v3"
            assert "estimated_size" in detail
        finally:
            app.dependency_overrides.pop(get_request_user, None)
